package com.minortheorie3.sudoku;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Color;

public class Spel {
	private Vak[][] vakjes;
	private ArrayList<Box> boxes;

	private int aantalRijen = 9;
	private int aantalKolommen = 9;

	String errorMessage;

	/**
	 * Verander het getal in het vakje met de gekozen getal bij een geldige zet
	 * 
	 * @param getal
	 * @param vakje
	 * @throws Exception
	 */
	public void changeGetalVakje(int getal, Vak vakje) throws Exception {
		boolean geldigeZet = valideerZet(getal, vakje);
		if (geldigeZet) {
			vakje.setGetal(getal);
			setVakje(vakje.getRij(), vakje.getKolom(), vakje);
		} else {
			throw new Exception(errorMessage);
		}
	}

	public int getAantalRijen() {
		return aantalRijen;
	}

	public int getAantalKolommen() {
		return aantalKolommen;
	}

	/**
	 * Vakjes vullen
	 */
	public void vakjesVullen(Context context) {
		vakjes = new Vak[aantalRijen][aantalKolommen];
		int[][] spelGetallen = genereerGetallenSpelbord();
		// boxes initialiseren
		initializeBoxes();

		for (int rij = 0; rij < aantalRijen; rij++) {
			for (int kolom = 0; kolom < aantalKolommen; kolom++) {
				// bepaal de waardes
				int colorCode = bepaalKleur(rij, kolom);
				int getal = 0;
				boolean veranderbaar = true;

				// bestaande getallen van het spel
				if (spelGetallen[rij][kolom] != 0) {
					getal = spelGetallen[rij][kolom];
					veranderbaar = false;
				}

				// box vullen
				int boxNummer = getBoxNummer(rij, kolom);
				Box box = getBox(boxNummer);
				if (box == null) {
					box = new Box(getAantalRijen(), getAantalKolommen(),
							boxNummer);
				}
				Vak vakje = new Vak(context, rij, kolom, getal, colorCode, box);
				vakje.setVeranderbaar(veranderbaar);
				box.setVakje(vakje);

				// add to the collections
				vakjes[rij][kolom] = vakje;
				boxes.set(boxNummer, box);
			}
		}

		System.out.println("dsfsdaf");
	}

	/**
	 * Haalt de box op met de opgegeven index
	 * 
	 * @param index
	 * @return
	 */
	public Box getBox(int index) {
		return boxes.get(index);
	}

	/**
	 * Get de vakjes
	 * 
	 * @return
	 */
	public Vak[][] getVakjes() {
		return vakjes;
	}

	/**
	 * Geef het vakje terug op de positie van rij en kolom
	 * 
	 * @param rij
	 * @param kolom
	 * @return
	 */
	public Vak getVakje(int rij, int kolom) {
		return vakjes[rij][kolom];
	}

	/**
	 * Kijken of het getal al niet voorkomt in dezelfde rij, kolom of box
	 * 
	 * @param getal
	 * @param vakje
	 * @return
	 */
	private boolean valideerZet(int getal, Vak vakje) {
		// check rij
		ArrayList<Vak> vakjesRij = getVakjesRij(vakje.getRij());
		for (Vak vak : vakjesRij) {
			if (vak.getGetal() == getal) {
				errorMessage = "Het getal bestaat al in dezelfde rij!";
				return false; // dezelfde getal ontdekt, dus geeft validatie
								// false terug
			}
		}
		// check kolom
		ArrayList<Vak> vakjesKolom = getVakjesKolom(vakje.getKolom());
		for (Vak vak : vakjesKolom) {
			if (vak.getGetal() == getal) {
				errorMessage = "Het getal bestaat al in dezelfde kolom!";
				return false;
			}
		}
		// check box
		Box box = vakje.getBox();
		for (Vak vak : box.getVakjes()) {
			if (vak.getGetal() == getal) {
				errorMessage = "Het getal bestaat al in dezelfde box!";
				return false;
			}
		}
		return true;
	}

	/**
	 * Get de vakjes van een bepaalde rij
	 * 
	 * @param rij
	 * @return
	 */
	public ArrayList<Vak> getVakjesRij(int rij) {
		ArrayList<Vak> rijVakjes = new ArrayList<Vak>();
		for (int kolom = 0; kolom < getAantalKolommen(); kolom++) {
			rijVakjes.add(getVakje(rij, kolom));
		}
		return rijVakjes;
	}

	/**
	 * Get de vakjes van een bepaalde kolom
	 * 
	 * @param kolom
	 * @return
	 */
	private ArrayList<Vak> getVakjesKolom(int kolom) {
		ArrayList<Vak> kolomVakjes = new ArrayList<Vak>();
		for (int rij = 0; rij < getAantalRijen(); rij++) {
			kolomVakjes.add(getVakje(rij, kolom));
		}
		return kolomVakjes;
	}

	/**
	 * Zet het vakje in het vakjes array van het spel
	 * 
	 * @param rij
	 * @param kolom
	 * @param vakje
	 */
	public void setVakje(int rij, int kolom, Vak vakje) {
		vakjes[rij][kolom] = vakje;
	}

	/**
	 * Bepaal de kleur van een vakje
	 * 
	 * @param rij
	 * @param kolom
	 * @return
	 */
	private int bepaalKleur(int rij, int kolom) {
		int kleurCode = Color.GRAY;
		int boxNummer = getBoxNummer(rij, kolom);

		switch (boxNummer) {
		case 0:
		case 6:
			kleurCode = Color.BLUE;
			break;
		case 1:
		case 7:
			kleurCode = Color.RED;
			break;
		case 2:
		case 8:
			kleurCode = Color.MAGENTA;
			break;
		case 3:
		case 5:
			kleurCode = Color.GREEN;
			break;
		case 4:
			kleurCode = Color.GRAY;
			break;
		}
		return kleurCode;
	}

	private int getBoxNummer(int rij, int kolom) {
		int boxNummer = 0;
		switch (rij) {
		case 0:
		case 1:
		case 2:
			if (kolom == 0 || kolom == 1 || kolom == 2)
				boxNummer = 0;
			if (kolom == 3 || kolom == 4 || kolom == 5)
				boxNummer = 1;
			if (kolom == 6 || kolom == 7 || kolom == 8)
				boxNummer = 2;
			break;
		case 3:
		case 4:
		case 5:
			if (kolom == 0 || kolom == 1 || kolom == 2)
				boxNummer = 3;
			if (kolom == 3 || kolom == 4 || kolom == 5)
				boxNummer = 4;
			if (kolom == 6 || kolom == 7 || kolom == 8)
				boxNummer = 5;
			break;
		case 6:
		case 7:
		case 8:
			if (kolom == 0 || kolom == 1 || kolom == 2)
				boxNummer = 6;
			if (kolom == 3 || kolom == 4 || kolom == 5)
				boxNummer = 7;
			if (kolom == 6 || kolom == 7 || kolom == 8)
				boxNummer = 8;
			break;
		}
		return boxNummer;
	}

	private void initializeBoxes() {
		boxes = new ArrayList<Box>();
		boxes.add(null);
		boxes.add(null);
		boxes.add(null);
		boxes.add(null);
		boxes.add(null);
		boxes.add(null);
		boxes.add(null);
		boxes.add(null);
		boxes.add(null);
	}

	/**
	 * Het genereren van de getallen op het spelbord
	 * 
	 * @return
	 */
	private int[][] genereerGetallenSpelbord() {
		int[][] spelbordGetallen = new int[getAantalRijen()][getAantalKolommen()];

		// zet eerste alle getallen op 0
		for (int rij = 0; rij < aantalRijen; rij++) {
			for (int kolom = 0; kolom < aantalKolommen; kolom++) {
				spelbordGetallen[rij][kolom] = 0;
			}
		}
		// TODO: Ibtissam taak om getallen random aan te maken met een algoritme

		spelbordGetallen = handmatigIngevuld(spelbordGetallen);
		return spelbordGetallen;
	}

	private int[][] handmatigIngevuld(int[][] spelbordGetallen) {
		// handmatig de getallen inzetten
		spelbordGetallen[0][2] = 5;
		spelbordGetallen[0][3] = 4;
		spelbordGetallen[0][4] = 2;
		spelbordGetallen[0][5] = 9;
		spelbordGetallen[0][6] = 3;

		spelbordGetallen[1][1] = 7;
		spelbordGetallen[1][7] = 6;

		spelbordGetallen[2][1] = 9;
		spelbordGetallen[2][2] = 8;
		spelbordGetallen[2][6] = 2;
		spelbordGetallen[2][7] = 4;

		spelbordGetallen[3][3] = 2;
		spelbordGetallen[3][4] = 3;
		spelbordGetallen[3][5] = 1;

		spelbordGetallen[5][3] = 7;
		spelbordGetallen[5][4] = 6;
		spelbordGetallen[5][5] = 5;

		spelbordGetallen[6][1] = 6;
		spelbordGetallen[6][2] = 7;
		spelbordGetallen[6][6] = 9;
		spelbordGetallen[6][7] = 3;

		spelbordGetallen[7][1] = 5;
		spelbordGetallen[7][7] = 2;

		spelbordGetallen[8][2] = 9;
		spelbordGetallen[8][3] = 6;
		spelbordGetallen[8][4] = 1;
		spelbordGetallen[8][5] = 8;
		spelbordGetallen[8][6] = 7;

		return spelbordGetallen;
	}

	public int[][] getSolution() {
		int[][] sol = new int[9][9];
		sol[0][0] = 6;
		sol[0][1] = 1;
		sol[0][2] = 5;
		sol[0][3] = 4;
		sol[0][4] = 2;
		sol[0][5] = 9;
		sol[0][6] = 3;
		sol[0][7] = 8;
		sol[0][8] = 7;
		sol[1][0] = 4;

		sol[1][1] = 7;

		sol[1][2] = 2;

		sol[1][3] = 8;

		sol[1][4] = 5;

		sol[1][5] = 3;

		sol[1][6] = 1;

		sol[1][7] = 6;

		sol[1][8] = 9;

		sol[2][0] = 3;

		sol[2][1] = 9;

		sol[2][2] = 8;

		sol[2][3] = 1;

		sol[2][4] = 7;

		sol[2][5] = 6;

		sol[2][6] = 2;

		sol[2][7] = 4;

		sol[2][8] = 5;

		sol[3][0] = 7;

		sol[3][1] = 4;

		sol[3][2] = 6;

		sol[3][3] = 2;

		sol[3][4] = 3;

		sol[3][5] = 1;

		sol[3][6] = 5;

		sol[3][7] = 9;

		sol[3][8] = 8;

		sol[4][0] = 5;

		sol[4][1] = 2;

		sol[4][2] = 1;

		sol[4][3] = 9;

		sol[4][4] = 8;

		sol[4][5] = 4;

		sol[4][6] = 6;

		sol[4][7] = 7;

		sol[4][8] = 3;

		sol[5][0] = 9;

		sol[5][1] = 8;

		sol[5][2] = 3;

		sol[5][3] = 7;

		sol[5][4] = 6;

		sol[5][5] = 5;

		sol[5][6] = 4;

		sol[5][7] = 1;

		sol[5][8] = 2;

		sol[6][0] = 8;

		sol[6][1] = 6;

		sol[6][2] = 7;

		sol[6][3] = 5;

		sol[6][4] = 4;

		sol[6][5] = 2;

		sol[6][6] = 9;

		sol[6][7] = 3;

		sol[6][8] = 1;

		sol[7][0] = 1;

		sol[7][1] = 5;

		sol[7][2] = 4;

		sol[7][3] = 3;

		sol[7][4] = 9;

		sol[7][5] = 7;

		sol[7][6] = 8;

		sol[7][7] = 2;

		sol[7][8] = 6;

		sol[8][0] = 2;

		sol[8][1] = 3;

		sol[8][2] = 9;

		sol[8][3] = 6;

		sol[8][4] = 1;

		sol[8][5] = 8;

		sol[8][6] = 7;

		sol[8][7] = 5;

		sol[8][8] = 4;

		return sol;

	}

	/**
	 * Check of einde van de spel zijn
	 * @return
	 */
	public boolean checkEindspel() {
		for (int rij = 0; rij < aantalRijen; rij++) {
			for (int kolom = 0; kolom < aantalKolommen; kolom++) {
				if (vakjes[rij][kolom].getGetal() == 0)
					return false;
			}
		}
		return true;
	}
	
	/**
	 * Controleert of het spel is afgelopen en gewonnen
	 * 
	 * @return
	 */
	public boolean checkIsGewonnen() {

		// TODO: als het laatste hokje is ingevuld, controleer dan of iemand
		// gewonnnen is.

		int[][] sol = getSolution();

		for (int rij = 0; rij < aantalRijen; rij++) {

			for (int kolom = 0; kolom < aantalKolommen; kolom++) {

				if (vakjes[rij][kolom].getGetal() != sol[rij][kolom])

				{

					return false;

				}

			}

		}

		return true;

	}

}
